<?php
require_once(dirname(__FILE__) ."/Error.php");
require_once(dirname(__FILE__) ."/SessionManager.php");

/**
 * Captcha
 *
 * Captcha class to use in forms
 * @author King Beto <apardo@adverit.com>
 * @version 1.0
 * @update 3/4/2008 4:33:00 PM
 *
 * Here is an example
 * <code>
 * <?
 * // On your form page add the following lines:
 * require_once(DIR_CLASSES ."/Captcha.php");
 * $captcha = new Captcha("Please complete the Captcha:");
 * if ($captcha->create())
 * {
 *     echo $captcha->htmlImage;
 *     echo $captcha->htmlInput;
 * }
 *
 * // In your form, where you want the Captcha displayed add this:
 * echo $captchaInput;
 * ?>
 *
 * <?
 * // On the processing page add this:
 * require_once(DIR_CLASSES ."/Captcha.php");
 * $captcha = new Captcha();
 * $verified = $captcha->verify($_POST[$captcha->captchaInputName]);
 *
 * In your processing check for $verified being true/false.
 * if($verified) echo "Captcha was correct";
 * else "Captcha was incorrect";
 * // Thats it! :)
 * </code>
 *
 * @package AdMin Framework
 */

class Captcha extends Error
{
    /* Variables */
    public $tmpDir       = ''; // No forward slash - must be writable, chmod 777
    public $tmpUrl       = ''; // No forward slash.
    public $captchaLabel = ''; // This is what is shown just above the captcha image.

    /* Advanced Users Variables */
    public $captchaLength    = 5;
    public $imageWidth       = 115;
    public $imageHeight      = 25;
    public $imageNumOfLines  = 8;
    public $imageTextColor   = array(0,0,0); // RGB (Black)
    public $imageLineColor   = array(255,20,147); // RGB (Black)
    public $imageBGColor     = array(176,226,255); // RBG (Light Blue)
    public $captchaInputName = 'catpcha';
    public $sessionVarName   = 'AdVCaptcha';
    public $cookieTimeout    = 300; // 5 mins.
    public $cleanupImages    = true;
    public $htmlImage        = '';
    public $htmlInput        = '';

    private $sessionManager;

    public function __construct($captchaLabel, $tmpDir='', $tmpUrl='')
    {
        $this->captchaLabel = $captchaLabel;
        $this->tmpDir = $tmpDir? $tmpDir : DIR_TMP ."/captcha";
        $this->tmpUrl = $tmpUrl? $tmpUrl : $GLOBALS["CONF"]["url"]["dir_file_tmp"] ."/captcha";
        $this->sessionManager = new SessionManager();
    }

    // Check the directories exist and are writeable.
    public function create()
    {
        // Cleanup before doing anything.
        $this->cleanup();

        // Define the dataset and set the key.
        $dataset = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','p','q','r','s','t','u','v','w','x','y','z',1,2,3,4,5,6,7,8,9);
        shuffle($dataset);
        $key = ''; // Initialise key.
        for($i=0; $i<$this->captchaLength; $i++)
        {
            $key .= strtoupper(trim($dataset[rand(0, count($dataset))]));
        }

        // Prep and generate the key hash - sha1.
        $key = trim($key);
        $keyHash = sha1($key);

        // Generate the image.
        $imageName = $this->generateImage($key);
        if(!$imageName)
        {
            $this->_error('There was an error generating the Captcha - Check your directory path and permissions.');
            return 0;
        }
        else
        {
            // Set the session var
            $this->sessionManager->set($this->sessionVarName, $keyHash);
            // Return the captcha and input box.
            $this->htmlImage = '<img src="'.$this->tmpUrl.'/'.$imageName.'" width="'.$this->imageWidth.'" height="'.$this->imageHeight.'" alt="Captcha" class="captcha">';
            $this->htmlInput = '<input type="text" size="22" name="'.$this->captchaInputName.'" value="">';
            return 1;
        }
    }

    public function generateImage($key)
    {
        // Create the image.
        $imageName = 'CAPTCHA_'.(time()+$this->cookieTimeout).'_'.rand(1,101).'_'.rand(201,9007).'.gif';
        $imageFilename = $this->tmpDir .'/'. $imageName;
        $image = imagecreate($this->imageWidth, $this->imageHeight);
        $background = imagecolorallocate($image, $this->imageBGColor[0], $this->imageBGColor[1], $this->imageBGColor[2]);

        // Set the text colour.
        $textColor = imagecolorallocate($image, $this->imageTextColor[0], $this->imageTextColor[1], $this->imageTextColor[2]);
        $lineColor = imagecolorallocate($image, $this->imageLineColor[0], $this->imageLineColor[1], $this->imageLineColor[2]);

        // Add some lines to screw over the form hackers.
        for($i=0;$i<$this->imageNumOfLines;$i++)
        {
            $this->randomLineCoords($x1, $y1, $x2, $y2);
            imageline($image, $x1, $y1, $x2, $y2, $lineColor);
        }

        // Put spaces in between the letters.
        $keySplit = str_split($key);
        $formattedKey = ''; // Initialise it.
        for($i=0; $i<$this->captchaLength; $i++) {
            $formattedKey .= ''.$keySplit[$i].' ';
        }

        // Add the text to the image.
        imagestring($image, 5, 20, 5, $formattedKey, $textColor);

        // Save the image.
        $saveImage = imagegif($image, $imageFilename, 100);
        imagedestroy($image);

        return $imageName? $imageName : false;
    }

    public function randomLineCoords(&$x1, &$y1, &$x2, &$y2)
    {
        $x1 = rand(0, $this->imageWidth);
        $y1 = rand(0, $this->imageHeight);
        $x2 = rand(0, $this->imageWidth);
        $y2 = rand(0, $this->imageHeight);
    }

    public function verify($userInput)
    {
        if($this->sessionManager->exists($this->sessionVarName))
        {   
            // Get the cookie and hash the user input.
            $cookieDataHash = trim($this->sessionManager->get($this->sessionVarName));
            $userInputF = strtoupper(trim($userInput));
            $userInputHash = sha1($userInputF);
            // Cleanup - the form has been submitted.
            $this->cleanup();

            // Verfiy that the cookie data length is 40 characters long - SHA1 standard - it might have been tampered with.
            if(strlen($cookieDataHash) == 40 && $cookieDataHash == $userInputHash)
                return 1;
        }
        return 0;
    }

    public function cleanup()
    {
        $this->sessionManager->delete($this->sessionVarName);
        if($openedDir = opendir($this->tmpDir))
        {
            while(($file = readdir($openedDir)) !== false)
            {
                if($file != "." && $file != "..")
                {
                    $filenameE = explode('_', $file);
                    $fileExpires = $filenameE[1];
                    if($fileExpires <= time())
                    {
                        unlink($this->tmpDir.'/'.$file);
                    }
                }
            }
        }
    }
}

?>
